﻿package glam.skins;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Rectangle;
import fpc.styles.skins.ButtonState;
import fpc.styles.skins.IScrollBarSkin;
import fpc.utils.IFactory;
import fpc.utils.Maths;
import fpc.utils.ScaledBitmap;
import glam.Primitives;

class GlamScrollBar implements IFactory<IScrollBarSkin>
{
	var _backgroundUp:BitmapData;
	
	var _backgroundDown:BitmapData;
	
	var _button:BitmapData;
	
	var _buttonDisabled:BitmapData;
	
	var _thumbIcon:BitmapData;
	
	var _arrowIcon:BitmapData;
	
	var _topColor:UInt;
	
	var _topAlpha:Float;
	
	public function new(primitives:Primitives = null)
	{
		if (primitives == null)
		{
			primitives = Primitives.defaultPrimitives;
		}
		
		_backgroundUp = primitives.getScrollBackgroundUp();
		_backgroundDown = primitives.getScrollBackgroundDown();
		_button = primitives.getButtonBackground();
		_buttonDisabled = primitives.getButtonBackgroundDisabled();
		_thumbIcon = primitives.getThumbIcon();
		_arrowIcon = primitives.getArrowIcon();
		_topColor = primitives.buttonBackgroundTopColor;
		_topAlpha = primitives.buttonBackgroundTopAlpha;
	}
	
	public function create():IScrollBarSkin
	{
		return new GlamScrollBarSkin(_backgroundUp, _backgroundDown, _button, _buttonDisabled, _thumbIcon, _arrowIcon, _topColor, _topAlpha);
	}
}

class GlamScrollBarSkin implements IScrollBarSkin
{
	private static var BUTTON_SIZE = 18;
	
	private static var MIN_THUMB_SIZE = 17;
	
	var _background:Bitmap;
	
	var _backgroundTop:Bitmap;
	
	var _thumbStateChanger:StateChanger;
	
	var _thumbIcon:Bitmap;
	
	var _leftArrow:Bitmap;
	
	var _rightArrow:Bitmap;
	
	var _leftStateChanger:StateChanger;
	
	var _rightStateChanger:StateChanger;
	
	var _bdBackgroundUp:BitmapData;
	
	var _bdBackgroundDown:BitmapData;
	
	public function new(
		bdBackgroundUp:BitmapData, bdBackgroundDown:BitmapData, bdButton:BitmapData, bdButtonDisabled:BitmapData,
		thumbIcon:BitmapData, arrowIcon:BitmapData,
		topColor:UInt, topAlpha:Float
	)
	{
		object = new Sprite();
		
		_bdBackgroundUp = bdBackgroundUp;
		_bdBackgroundDown = bdBackgroundDown;
		
		_background = new Bitmap(bdBackgroundUp);
		background = new Sprite();
		background.addChild(_background);
		object.addChild(background);
		
		thumb = new Sprite();
		object.addChild(thumb);
		var thumbBitmap = new ScaledBitmap(thumb.graphics, bdButton);
		var shape = new Shape();
		thumb.addChild(shape);
		_thumbStateChanger = new StateChanger(thumbBitmap, bdButton, bdButtonDisabled, shape, topColor, topAlpha);
		_thumbStateChanger.fixDownLeft = true;
		_thumbStateChanger.fixDownRight = true;
		_thumbIcon = new Bitmap(thumbIcon);
		thumb.addChild(_thumbIcon);
		
		left = new Sprite();
		object.addChild(left);
		var buttonBitmap = new ScaledBitmap(left.graphics, bdButton);
		var shape = new Shape();
		left.addChild(shape);
		_leftStateChanger = new StateChanger(buttonBitmap, bdButton, bdButtonDisabled, shape, topColor, topAlpha);
		_leftStateChanger.width = BUTTON_SIZE;
		_leftStateChanger.height = BUTTON_SIZE;
		_leftStateChanger.onDownChange = onLeftDownChange;
		_leftArrow = new Bitmap(arrowIcon);
		left.addChild(_leftArrow);
		
		right = new Sprite();
		object.addChild(right);
		var buttonBitmap = new ScaledBitmap(right.graphics, bdButton);
		var shape = new Shape();
		right.addChild(shape);
		_rightStateChanger = new StateChanger(buttonBitmap, bdButton, bdButtonDisabled, shape, topColor, topAlpha);
		_rightStateChanger.width = BUTTON_SIZE;
		_rightStateChanger.height = BUTTON_SIZE;
		_rightStateChanger.onDownChange = onRightDownChange;
		_rightArrow = new Bitmap(arrowIcon);
		right.addChild(_rightArrow);
	}
	
	function onLeftDownChange(value:Bool)
	{
		if (_horisontal)
		{
			_leftArrow.x += value ? -1 : 1;
		}
		else
		{
			_leftArrow.y += value ? -1 : 1;
		}
	}
	
	function onRightDownChange(value:Bool)
	{
		if (_horisontal)
		{
			_rightArrow.x += value ? 1 : -1;
		}
		else
		{
			_rightArrow.y += value ? 1 : -1;
		}
	}
	
	public var object(default, null):Sprite;
	
	public var thumb(default, null):Sprite;
	
	public var left(default, null):Sprite;
	
	public var right(default, null):Sprite;
	
	public var background(default, null):Sprite;
	
	public var minSize(default, null):Int;
	
	public var thickness(default, null):Int;
	
	public var thumbDragRect(default, null):Rectangle;
	
	public var size:Int;
	
	public var thumbPortion:Float;
	
	public var active:Bool;
	
	public var leftState:ButtonState;
	
	public var rightState:ButtonState;
	
	public var thumbState:ButtonState;
	
	public var backgroundDownLeft:Bool;
	
	public var backgroundDownRight:Bool;
	
	var _horisontal:Bool;
	
	var _backgroundSize:Int;
	
	public function init(horisontal:Bool)
	{
		thickness = BUTTON_SIZE;
		minSize = 55;
		thumbPortion = .5;
		
		_horisontal = horisontal;
		
		thumbDragRect = new Rectangle();
		_backgroundSize = Std.int(_background.width);
		
		if (horisontal)
		{
			background.x = 5;
			
			_leftArrow.rotation = 180;
			_leftArrow.x = 13;
			_leftArrow.y = 16;
			_rightArrow.x = 5;
			_rightArrow.y = 2;
			
			thumbDragRect.left = BUTTON_SIZE;
			
			_leftStateChanger.fixDownLeft = true;
			_leftStateChanger.fixDownTop = true;
			_leftStateChanger.fixDownBottom = true;
			
			_rightStateChanger.fixDownRight = true;
			_rightStateChanger.fixDownTop = true;
			_rightStateChanger.fixDownBottom = true;
		}
		else
		{
			thumb.rotation = 90;
			thumb.x = thickness;
			
			background.y = 5;
			background.x = thickness;
			background.rotation = 90;
			
			thumbDragRect.x = thickness;
			thumbDragRect.top = BUTTON_SIZE;
			
			_leftArrow.rotation = -90;
			_leftArrow.x = 2;
			_leftArrow.y = 13;
			_rightArrow.rotation = 90;
			_rightArrow.x = 16;
			_rightArrow.y = 5;
			
			_leftStateChanger.fixDownTop = true;
			_leftStateChanger.fixDownLeft = true;
			_leftStateChanger.fixDownRight = true;
			
			_rightStateChanger.fixDownBottom = true;
			_rightStateChanger.fixDownLeft = true;
			_rightStateChanger.fixDownRight = true;
		}
		
		active = true;
		
		leftState = NORMAL;
		rightState = NORMAL;
		thumbState = NORMAL;
	}
	
	var _oldBackgroundDownLeft:Bool;
	
	var _oldBackgroundDownRight:Bool;
	
	public function update()
	{	
		var areaSize = size - BUTTON_SIZE * 2;
		var thumbSize = Std.int(areaSize * thumbPortion);
		if (thumbSize < MIN_THUMB_SIZE)
		{
			thumbSize = MIN_THUMB_SIZE;
		}
		
		var maxPosition = size - BUTTON_SIZE - thumbSize;
		if (_horisontal)
		{
			_background.width = size - 10;
			right.x = size - BUTTON_SIZE;
			thumbDragRect.right = maxPosition;
			if (thumb.x < thumbDragRect.left)
			{
				thumb.x = thumbDragRect.left;
			}
			else if (thumb.x > thumbDragRect.right)
			{
				thumb.x = thumbDragRect.right;
			}
		}
		else
		{
			_background.scaleX = (size - 10) / _backgroundSize;
			right.y = size - BUTTON_SIZE;
			thumbDragRect.bottom = maxPosition;
			if (thumb.y < thumbDragRect.top)
			{
				thumb.y = thumbDragRect.top;
			}
			else if (thumb.y > thumbDragRect.bottom)
			{
				thumb.y = thumbDragRect.bottom;
			}
		}
		_thumbIcon.x = Std.int(thumbSize - _thumbIcon.width) >> 1;
		
		_thumbStateChanger.width = thumbSize;
		_thumbStateChanger.height = thickness;
		_thumbStateChanger.state = thumbState;
		_thumbStateChanger.update();
		
		_leftStateChanger.state = leftState;
		_leftStateChanger.update();
		
		_rightStateChanger.state = rightState;
		_rightStateChanger.update();
		
		thumb.visible = active;
		
		if (_oldBackgroundDownLeft != backgroundDownLeft || _oldBackgroundDownRight != backgroundDownRight)
		{
			if (backgroundDownLeft || backgroundDownRight)
			{
				if (_backgroundTop == null)
				{
					_backgroundTop = new Bitmap(_bdBackgroundDown);
					background.addChild(_backgroundTop);
				}
				_backgroundTop.visible = true;
			}
			else
			{
				if (_backgroundTop != null)
				{
					_backgroundTop.visible = false;
				}
			}
			_oldBackgroundDownLeft = backgroundDownLeft;
			_oldBackgroundDownRight = backgroundDownRight;
		}
		
		if (backgroundDownLeft || backgroundDownRight)
		{
			var min:Float;
			var max:Float;
			var thumbPosition = _horisontal ? thumb.x : thumb.y;
			if (backgroundDownLeft)
			{
				min = 0;
				max = thumbPosition + thumbSize * .5 - 5;
			}
			else
			{
				min = thumbPosition + thumbSize * .5 - 5;
				max = size - 10;
			}
			_backgroundTop.x = min;
			_backgroundTop.scaleX = (max - min) / _backgroundSize;
		}
	}
}

class StateChanger
{
	var _bdEnabled:BitmapData;
	
	var _bdDisabled:BitmapData;
	
	public var fixDownLeft:Bool;
	
	public var fixDownRight:Bool;
	
	public var fixDownTop:Bool;
	
	public var fixDownBottom:Bool;
	
	public function new(
		bitmap:ScaledBitmap, bdEnabled:BitmapData, bdDisabled:BitmapData, shape:Shape,
		topColor:UInt, topAlpha:Float
	)
	{
		_bitmap = bitmap;
		_bdEnabled = bdEnabled;
		_bdDisabled = bdDisabled;
		_shape = shape;
		
		var g = _shape.graphics;
		g.beginFill(topColor, topAlpha);
		g.drawRoundRect(0, 0, 20, 20, 6);
		g.endFill();
		_shape.scale9Grid = new Rectangle(5, 5, 10, 10);
		_shape.visible = false;
		
		_animation = 0;
	}
	
	public var width:Int;
	
	public var height:Int;
	
	public var state:ButtonState;
	
	public var onDownChange:Bool->Void;
	
	var _bitmap:ScaledBitmap;
	
	var _shape:Shape;
	
	var _needResize:Bool;
	
	var _oldWidth:Int;
	
	var _oldHeight:Int;
	
	var _oldState:ButtonState;
	
	public function update()
	{
		_needResize = false;
		
		if (state != _oldState)
		{
			_oldState = state;
			switch (state)
			{
				case NORMAL:
					setOver(false);
					setDown(false);
					setEnabled(true);
				case OVER:
					setOver(true);
					setDown(false);
					setEnabled(true);
				case DOWN:
					setDown(true);
					setOver(true);
					setEnabled(true);
				case DISABLED:
					setEnabled(false);
					setDown(false);
					setOver(false);
			}
		}
		
		if (_needResize || width != _oldWidth || height != _oldHeight)
		{
			var delta = _down ? 0 : 1;
			var left = fixDownLeft ? 1 : delta;
			var right = fixDownRight ? 1 : delta;
			var top = fixDownTop ? 1 : delta;
			var bottom = fixDownBottom ? 1 : delta;
			_bitmap.setBounds( -left, -top, width + left + right, height + top + bottom);
			_oldWidth = width;
			_oldHeight = height;
		}
	}
	
	static var ANIMATION_STEP = .2;
	
	var _over:Bool;
	
	var _isOn:Bool;
	
	function setOver(value:Bool)
	{
		if (value == _over)
		{
			return;
		}
		
		_over = value;
		
		if (value)
		{
			if (_animation < 0)
			{
				_animation = 0;
			}
			_isOn = true;
			_shape.addEventListener(Event.ENTER_FRAME, onOverEnterFrame);
		}
		else
		{
			if (_animation > 1)
			{
				_animation = 1;
			}
			_isOn = false;
			_shape.addEventListener(Event.ENTER_FRAME, onOverEnterFrame);
		}
	}
	
	function onOverEnterFrame(event:Event)
	{
		if ((_animation += _isOn ? ANIMATION_STEP : -ANIMATION_STEP) > 1)
		{
			_animation = 1;
			_shape.removeEventListener(Event.ENTER_FRAME, onOverEnterFrame);
		}
		else if (_animation < 0)
		{
			_animation = 0;
			_shape.removeEventListener(Event.ENTER_FRAME, onOverEnterFrame);
		}
		updateTopAnimation();
	}
	
	var _animation:Float;
	
	function updateTopAnimation()
	{
		var shape = _shape;
		if (_animation <= .00001)
		{
			shape.visible = false;
		}
		else
		{			
			shape.visible = true;
			var k = Math.sqrt(_animation);
			var maxBorder = Maths.intMin(width, height) * .4;
			
			shape.alpha = _isOn
				? .8
				: k * .6 + .2;
			var minBorder = 2;
			var border = minBorder + (maxBorder - minBorder) * (1 - k);
			shape.width = width - border * 2;
			shape.height = height - border * 2;
			shape.x = border;
			shape.y = border;
		}
	}
	
	var _down:Bool;
	
	function setDown(value:Bool)
	{
		if (value == _down)
		{
			return;
		}
		
		_down = value;
		
		_needResize = true;
		
		if (onDownChange != null)
		{
			onDownChange(value);
		}
	}
	
	var _enabled:Bool;
	
	var _secodEnabledChange:Bool;
	
	function setEnabled(value:Bool)
	{
		if (value == _enabled && _secodEnabledChange)
		{
			return;
		}
		
		_secodEnabledChange = true;
		_enabled = value;
		_bitmap.bitmapData = value ? _bdEnabled : _bdDisabled;
	}
}